import json
import time
from datetime import timedelta

import requests
from loguru import logger
from topsdk.client import TopApiClient, TopException
from topsdk.defaultability.defaultability import Defaultability

from topsdk.defaultability.request.alitrip_policy_special_upload_request import *
from tqdm import tqdm


# -*- coding: utf-8 -*-
# @Author : HJH
# @Time : 2024/1/23 14:06
# @Use : 飞猪特殊政策上传


def fz_process_row(row, pr_param, sale_mode_code):
    """
    定义一个函数，传入一行数据，转化为PolicyDto需要导入的政策
    :param sale_mode_code: 销售类型
    :param is_share: 是否转换共享政策
    :param pr_param: 输入参数
    :param row:飞猪模板数据
    :return:飞猪接口需要的PolicyDto数据
    """
    if not isinstance(pr_param, dict):
        RuntimeError("配置参数有误，请检查后重新启动")

    is_share = 0
    if 'GX' in pr_param.get('platform'):
        is_share = 1

    return_price = pr_param.get('return_price', 0)
    bidd_fee = pr_param.get('bidd_fee', 0)
    fare_price_add = pr_param.get('fare_price_add', 0)
    if sale_mode_code != "0":
        return_price = pr_param.get('db_return_price', 0)
        bidd_fee = pr_param.get('db_bidd_fee', 0)
        fare_price_add = pr_param.get('db_fare_price_add', 0)

    """
    行程信息
    """
    flightDTO = AlitripPolicySpecialUploadFlightDTO()
    # 允许航班号
    flightDTO.allow_flight_num = str(row.get('flightNo')[-4:] if len(row.get('flightNo')) == 6 else row.get('flightNo'))
    # 去程舱位
    flightDTO.cabin = row.get('cabin')
    # 去程班期限制
    flightDTO.day_week = [1, 2, 3, 4, 5, 6, 7]
    # 行程类型标记：0，单程；1，往返
    flightDTO.flight_index = 0
    """
    允许航班日期
    """
    allowTravelDate = AlitripPolicySpecialUploadDatePairDto()
    # 根据"-"进行分割字符串
    allowTravelDate.start = datetime.strptime(row.get('depDate'), "%Y-%m-%d").replace(hour=00, minute=00,
                                                                                      second=00)
    allowTravelDate.end = datetime.strptime(row.get('depDate'), "%Y-%m-%d").replace(hour=23, minute=59, second=59)
    flightDTO.allow_travel_date = [allowTravelDate]

    """
    竞价信息
    """
    priceDto = AlitripPolicySpecialUploadPriceDto()
    # 代理费率
    priceDto.commission = 0
    # 留钱
    priceDto.return_price = int(return_price * 100)
    # 儿童价计算方法
    priceDto.child_sale_type = pr_param.get('child_sale_type')
    # 免费托运行李额
    priceDto.baggage = 0
    # 固定金额竞价空间
    priceDto.bidd_fee = bidd_fee * 100

    """
    销售信息
    """
    saleDto = AlitripPolicySpecialUploadSaleDto()
    # 提前预定天数 只能输入A-B的格式，并且A、B都是0到365之间的整数
    saleDto.advance_day = pr_param.get('advance_day')
    # 销售方式 0 无， 1 打包销售套餐1， 2 打包销售套餐2， 3 打包销售套餐3， 4 返现-航司运价，5 返现-销售方包装
    saleDto.sale_mode_code = int(sale_mode_code)
    """
    销售日期
    """
    saleDate = AlitripPolicySpecialUploadDatePairDto()
    # 根据"-"进行分割字符串
    saleDate.start = datetime.now().replace(hour=00, minute=00, second=00)
    saleDate.end = datetime.now().replace(month=12, day=31, hour=23, minute=59, second=59)
    saleDto.sale_date = [saleDate]

    """
    乘机人信息
    """
    passengerDto = AlitripPolicySpecialUploadPassengerDto()
    # 产品类型
    passengerDto.product_code = 0
    if pr_param.get('card_type', 0) == 1:
        passengerDto.documents_type = [1]

    """
    需要导入的政策列表
    """
    policyDto = AlitripPolicySpecialUploadPolicyDto()
    # 政策id
    policyDto.policy_code = pr_param.get('policyCode')
    if pr_param.get('policy_of_cabin', 0) == 1:
        if row.get('cabin') in 'N,T,Z,V,E,H,U,L,A,M,B,Y':
            policyDto.policy_code = (policyDto.policy_code + '0')
        elif row.get('cabin') in 'R':
            policyDto.policy_code = (policyDto.policy_code + '1')
    # 政策状态：1，有效；2，挂起；0，删除 3，停用
    policyDto.status = 1
    # policyDto.status = 2
    # 航司代码
    policyDto.airline = str(row.get('flightNo')[:2])
    # 到达机场三字码
    policyDto.arr_airport = row.get('arr')
    # 是否订位
    policyDto.create_pnr = pr_param.get('create_pnr')
    # 出发机场三字码
    policyDto.dep_airport = row.get('dep')
    # 商家配置号
    policyDto.office_no = 'CGO104'
    # 是否需要pata验价
    policyDto.pata = 0
    # 是否支持代码共享航班
    policyDto.code_share = is_share
    # 共享航班号码
    shareFlightNo = row.get('shareFlightNo', None)
    policyDto.code_share_airline = shareFlightNo[:2] if shareFlightNo else None
    # 票面价
    policyDto.fare_price = int(float(row.get('costPrice')) + float(fare_price_add))
    # 政策备注
    policyDto.memo = row.get('price').split('.')[0] + pr_param.get('policy_note')
    # 直达中转
    policyDto.direct_transfer_type = 0
    # 是否紧张停售
    policyDto.urgent_stop_sell = pr_param.get('urgent_stop_sell')
    # 外放舱位数量小于等于阈值时订位或停售
    policyDto.create_pnr_limit = pr_param.get('create_pnr_limit')
    policyDto.passenger = passengerDto
    policyDto.sale = saleDto
    policyDto.price = priceDto
    policyDto.flights = [flightDTO]
    return policyDto


def add_fz_policy(pds: list, agent_id: int):
    """
    普通政策上传参数
    :param agent_id:
    :param pds:
    :return:
    """
    policyCreateRequestDto = AlitripPolicySpecialUploadPolicyCreateRequestDto()
    # 行程类型
    policyCreateRequestDto.trip_type = 0
    # 店铺id
    policyCreateRequestDto.agent_id = agent_id
    # 操作类型：ADD，增量；FULL，全量
    policyCreateRequestDto.exec_type = 'ADD'
    # 政策类型：P，普通政策；T，特殊政策；G，规则政策；
    policyCreateRequestDto.policy_type = 'T'
    # 商家传入时间
    policyCreateRequestDto.agent_send_time = datetime.now()
    # 需要导入的政策列表
    policyCreateRequestDto.policy_list = pds
    policyCreateRequestDto.delete_policy = []

    """
    将参数转为二进制文件的数据
    """
    request = AlitripPolicySpecialUploadRequest()
    request.param_policy_create_request_d_t_o = policyCreateRequestDto
    return request


def del_fz_policy(pc, agent_id, fuzzy_query: bool, del_p_list):
    """
    删除政策
    """
    deletePolicyList = []
    if del_p_list is not None:
        for del_p in del_p_list:
            deletePolicy = AlitripPolicySpecialUploadPolicyQueryParamDTO()
            deletePolicy.policy_code = pc
            # 根据政策编码模糊查询
            deletePolicy.fuzzy_query = fuzzy_query
            # 政策状态
            # deletePolicy.status = 2
            # 航司二字码
            # deletePolicy.airline = str(del_p.get('flightNo')[:2])
            # 出发机场三字码
            deletePolicy.dep_airport = del_p.get('dep')
            # 到达机场三字码
            deletePolicy.arr_airport = del_p.get('arr')
            # 去程舱位
            # deletePolicy.cabin_list1 = del_p.get('cabin')
            # 行程起始日期
            deletePolicy.travel_start_date = datetime.strptime(del_p.get('depDate'), "%Y-%m-%d").replace(hour=00,
                                                                                                         minute=00,
                                                                                                         second=00)
            # 行程截止日期
            deletePolicy.travel_end_date = datetime.strptime(del_p.get('depDate'), "%Y-%m-%d") + timedelta(days=1)
            deletePolicyList.append(deletePolicy)
    else:
        deletePolicy = AlitripPolicySpecialUploadPolicyQueryParamDTO()
        deletePolicy.policy_code = pc
        # 根据政策编码模糊查询
        deletePolicy.fuzzy_query = fuzzy_query
        deletePolicyList.append(deletePolicy)

    policyCreateRequestDto = AlitripPolicySpecialUploadPolicyCreateRequestDto()
    # 行程类型
    policyCreateRequestDto.trip_type = 0
    # 店铺id
    policyCreateRequestDto.agent_id = agent_id
    # 操作类型：ADD，增量；FULL，全量
    policyCreateRequestDto.exec_type = 'ADD'
    # 政策类型：P，普通政策；T，特殊政策；G，规则政策；
    policyCreateRequestDto.policy_type = 'T'
    # 商家传入时间
    policyCreateRequestDto.agent_send_time = datetime.now()
    # # 删除政策查询参数，需要删除时execType必须为ADD
    policyCreateRequestDto.delete_policy = deletePolicyList
    policyCreateRequestDto.policy_list = []

    """
    将参数转为二进制文件的数据
    """
    request = AlitripPolicySpecialUploadRequest()
    request.param_policy_create_request_d_t_o = policyCreateRequestDto
    return request


def send_fz_request(request, agent_id):
    """
    发送请求
    :return:
    """
    # create Client
    appkey = '25346003'
    app_sercet = 'aeda58e87f78ea981a141ac507e5a940'
    session = '6102a098713e7108013e55b21b78e46c0f649d29cd2fedf4259705970'
    if agent_id == 5533:
        appkey = '34288401'
        app_sercet = '2374ca014bf328d08033733f74f9ec46'
        session = '61024256fc983f8b5c1042cd2a10f13fd17289119b9015e2215223025450'
    client = TopApiClient(appkey=appkey, app_sercet=app_sercet,
                          top_gateway_url='http://39.98.61.147/huifei/',
                          verify_ssl=False)
    ability = Defaultability(client=client)

    # 将 policy_request 转换为 JSON 字符串
    # json_data = json.dumps(request.to_dict())

    """
    请求接口
    """
    max_tries = 5  # 最大尝试次数
    num_tries = 0  # 已尝试次数
    sleep_time2 = 3  # 设置等待时间
    while num_tries < max_tries:
        try:
            response = ability.alitrip_policy_special_upload(request, session)
            flag1 = response.get('result', {}).get('data', {}).get('create_result', {}).get('success')
            flag2 = response.get('result', {}).get('data', {}).get('delete_result', {}).get('success')
            if not (flag1 or flag2):
                num_tries += 1
                if num_tries >= max_tries:
                    # 如果已经尝试到最大次数，跳出循环，放弃重试
                    logger.error(f"重试{max_tries}次后仍然失败，放弃重试。")
                    break
                continue
            return  # 成功请求到数据，直接返回
        except (TopException, requests.exceptions.RequestException) as e:
            logger.error(e)
            num_tries += 1
            if num_tries >= max_tries:
                # 如果已经尝试到最大次数，跳出循环，放弃重试
                logger.error(f"重试{max_tries}次后仍然失败，放弃重试。")
                break
            # 打印日志，并且睡眠
            logger.info(f"请求失败，休眠 {sleep_time2} 秒后进行第 {num_tries + 1} 次尝试。")
            time.sleep(sleep_time2)


def run_fz_gx_upload(ru_data_res, ru_param):
    if not isinstance(ru_param, dict):
        RuntimeError("配置参数有误，请检查后重新启动")

    # 销售类型
    sale_mode_codes = ru_param.get('sale_mode_code').split(',')

    # 打开 JSON 文件
    with open('./config/share_flight.json') as f:
        # 加载 JSON 数据并转换为字典
        share_flight_data = json.load(f)

    policyDtoList = []
    share_platforms = ru_param.get('share_platform').split(',')
    for item in ru_data_res:
        # 获取共享标识 整理出共享航班对
        share_flight = []
        for share_platform in share_platforms:
            if share_platform in share_flight_data:
                share_flight.extend(share_flight_data.get(share_platform))

        # 共享特殊处理
        share_flight = [sfd for sfd in share_flight if sfd['main_flight'] == item['flightNo']]

        # 设置多种销售方式
        for sale_mode_code in sale_mode_codes:

            for sf in share_flight:
                item['shareFlightNo'] = sf['main_flight']
                item['flightNo'] = sf['share_flight']

                # 根据舱位复制
                if ru_param.get('copy_of_cabin', None) is not None:
                    cabins = ru_param.get('copy_of_cabin').split(',')
                    for cabin in cabins:
                        item['cabin'] = cabin
                        policyDto = fz_process_row(item, ru_param, sale_mode_code)
                        policyDtoList.append(policyDto)
                else:
                    policyDto = fz_process_row(item, ru_param, sale_mode_code)
                    policyDtoList.append(policyDto)

    size = 400
    total = len(policyDtoList)
    logger.info(f"【上传{ru_param.get('platform')}政策数据长度】: {total}")
    if total == 0:
        return
    logger.info("【飞猪GX特殊政策上传】: 请求中... ...")
    # 创建进度条
    progress_bar = tqdm(total=total, desc="执行进度", unit="次")
    for i in range(0, total, size):
        try:
            # 取出最大450条数据，添加到结果列表
            end = i + size
            subset = policyDtoList[i:end]
            # 获取请求参数
            agent_id = ru_param.get('agent_id', 5252)
            add_req = add_fz_policy(subset, agent_id)
            send_fz_request(add_req, agent_id)
        except Exception as e:
            logger.error(f"【飞猪GX特殊政策上传异常】: {str(e)}")
        # 更新进度条
        progress_bar.update(size)

    # 关闭进度条
    progress_bar.close()
    logger.info(f"【飞猪GX特殊政策上传成功】:{ru_param.get('policyCode')}")


def run_fz_upload(ru_data_res, ru_param):
    if not isinstance(ru_param, dict):
        RuntimeError("配置参数有误，请检查后重新启动")

    # 销售类型
    sale_mode_codes = ru_param.get('sale_mode_code').split(',')

    policyDtoList = []

    for item in ru_data_res:
        # 设置多种销售方式
        for sale_mode_code in sale_mode_codes:
            # 根据舱位复制
            if ru_param.get('copy_of_cabin', None) is not None:
                cabins = ru_param.get('copy_of_cabin').split(',')
                for cabin in cabins:
                    item['cabin'] = cabin
                    policyDto = fz_process_row(item, ru_param, sale_mode_code)
                    policyDtoList.append(policyDto)
            else:
                policyDto = fz_process_row(item, ru_param, sale_mode_code)
                policyDtoList.append(policyDto)

    size = 400
    total = len(policyDtoList)
    logger.info(f"【上传{ru_param.get('platform')}政策数据长度】: {total}")
    if total == 0:
        return
    logger.info("【飞猪特殊政策上传】: 请求中... ...")
    # 创建进度条
    progress_bar = tqdm(total=total, desc="执行进度", unit="次")
    for i in range(0, total, size):
        try:
            # 取出最大450条数据，添加到结果列表
            end = i + size
            subset = policyDtoList[i:end]
            # 获取请求参数
            agent_id = ru_param.get('agent_id', 5252)
            add_req = add_fz_policy(subset, agent_id)
            send_fz_request(add_req, agent_id)
        except Exception as e:
            logger.error(f"【飞猪特殊政策上传】: 异常: {str(e)}")
        # 更新进度条
        progress_bar.update(size)

    # 关闭进度条
    progress_bar.close()
    logger.info(f"【飞猪特殊政策上传成功】:{ru_param.get('policyCode')}")
